package com.kamikaze.docidset.impl;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;

public class AndDocIdSet extends DocIdSet implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  private static transient Log log = LogFactory.getLog(OrDocIdSet.class);
  	  
	
  public class DescDocIdSetComparator implements Comparator<DocIdSetIterator>, Serializable
  {
    private static final long serialVersionUID = 1L;
    public int compare(DocIdSetIterator o1, DocIdSetIterator o2)
    {
        return o2.doc() - o1.doc(); 
    }
  
  }
  
   private List<DocIdSet> sets = null;
   private List<DocIdSetIterator> docSets = null;
   private PriorityQueue<DocIdSetIterator> pq = null;
  
  public AndDocIdSet(List<DocIdSet> docSets)
  {
	  this.sets = docSets;
	  
  }
  
 class AndDocIdSetIterator extends DocIdSetIterator
  {
    int lastReturn = -1;
    int itCnt = 0;
    AndDocIdSetIterator() 
    {
        initialize();
    }

    private void initialize() 
    {
    	
    	docSets = new ArrayList<DocIdSetIterator>();
    	for(Iterator<DocIdSet> ii = sets.iterator();ii.hasNext();)
    	{
    		DocIdSet set=ii.next();
    		if (set!=null)
    		{
    			docSets.add(set.iterator());
    		}
    	}
    	
        if(docSets == null || docSets.size() < 1)
          throw new IllegalArgumentException("Minimum one iterator required");
        pq = new PriorityQueue<DocIdSetIterator>(docSets.size(), new DescDocIdSetComparator());
        for(int i=0;i<docSets.size();i++)
        {
          try
          {
            docSets.get(i).next();
            itCnt++;
            pq.offer(docSets.get(i));
          }
          catch (IOException e)
          {
        	log.error(e.getMessage(),e);
          }
         
        }
        docSets.clear();
        
    }

    @Override
    public int doc()
    {
      return lastReturn;
    }

    @Override
    public boolean next() throws IOException 
    {
    	
      if(itCnt > pq.size())
    	  return false;
      
      while(true)
      {
        
        DocIdSetIterator dcit = pq.poll();
        if(dcit == null || pq.isEmpty())
        	return false;
        int val = dcit.doc();
        docSets.clear();
        docSets.add(dcit);
        boolean concur = true;
        while(!pq.isEmpty())
        {
          dcit = pq.poll();
          if(dcit.skipTo(val) == false)
            return false;
          concur&= dcit.doc() == val?true:false;
          docSets.add(dcit);
        }
        
       
          if(concur)
          {
        	if(val == lastReturn)
        		continue;
        	
            lastReturn = val;
            for(int i=0;i<docSets.size();i++)
            {
              if(docSets.get(i).next())
              {
            	  pq.offer(docSets.get(i));
              }
             
            }
            //if(pq.size() == docSets.size())
            return true;
          }
          
          else
          {
            concur = true;
            // Did not meet concurrence move cursors forward.
            for(int i=0;i<docSets.size();i++)
            {
              pq.offer(docSets.get(i));
            }
            docSets.clear();
          }
       }
    }
  

    @Override
    public boolean skipTo(int target) throws IOException
    {
    	if(target == lastReturn)
    		return true;
    	else if(pq.size() < itCnt)
    		return false;
    	
    	docSets.clear();
    	DocIdSetIterator dcit = null;
   
    	while(!pq.isEmpty())
        {
    		
          dcit = pq.poll();
          if(!dcit.skipTo(target))
          { 
            return false;
          }
          docSets.add(dcit);
        }
    	
    	for(int i=0;i<docSets.size();i++)
    		pq.offer(docSets.get(i));
    
    	// just follow next logic from here.
    	return next();
       }
  }
  
  public DocIdSetIterator iterator()
  {
      return new AndDocIdSetIterator();
  } 
}
